Blog

7 Deluge Scripting Patterns Every Zoho Creator Developer Should Know

Loop-prevention flags, O(n²) subform fixes, custom sort fields, and other patterns I keep reaching for in production Zoho Creator applications — explained with real Deluge code.

PublishedJuly 2026
Read Time8 min
CategoryZoho Creator · Deluge
AuthorKunwar Pal
Introduction

Why Deluge patterns matter

Deluge is easy to pick up and easy to write yourself into a corner with. Most Zoho Creator apps start clean — a few forms, a few workflows — and then grow into something with dozens of interdependent fields, subforms with hundreds of rows, and scripts that quietly re-trigger each other.

The patterns below aren't theoretical. Each one comes from a real production issue I hit while building multi-module Zoho Creator applications — things like an infinite-loop bug that only appeared with certain field combinations, or a history-tracking script that worked fine at 50 rows and then timed out at 500.

💡 If you want to see several of these patterns applied together inside a real 15-module system, I wrote a full case study on a Solar EPC project management build that covers this in depth.
1

Stop self-triggered infinite loops with a "No Script Run" flag

The Problem
When a script programmatically updates a field, Zoho fires that field's own "on user input" handler — even though no user touched it. If that handler's logic modifies the same field again, you get a loop that either times out or silently re-runs itself dozens of times.

The fix is a boolean flag field that the script sets right before it changes the field in question. The handler checks the flag first thing — if it's true, it resets the flag and exits immediately, skipping its own logic.

Deluge — Loop prevention flag
// Before the script changes the field programmatically: input.No_Script_Run = true; input.Select_Component = updated_components; // Inside that field's own input handler: if(input.No_Script_Run == true) { input.No_Script_Run = false; return; // script-triggered, not user-triggered — skip } // else: genuine user input — run normally refreshComponentLogic(input);

This is the single most useful pattern for any form where two or more fields update each other. Without it, these mutual-update relationships are a matter of when, not if, they loop.

2

Replace O(n²) subform history checks with an event log

The Problem
A common way to track "what changed" is comparing every row in the current subform against every row in a historical copy — a nested loop. This works fine at 20 rows. At 500 rows, that's 250,000 comparisons, which blows past Zoho's script execution limits.
The Fix
Instead of comparing two snapshots after the fact, log changes as they happen. Create a blank subform that acts as an append-only event log — on every field input, write the field name, form name, record ID, and new value as a new row. Reading history back is now a single pass through the log, not a nested comparison.

This flips the problem from "detect what changed by comparing two states" to "record what changed at the moment it changes" — which is both cheaper and gives you a genuine audit trail as a side effect.

3

Sort subform rows with a computed field, not code

Sorting subform rows in script — grouping by category, then by sub-order — usually means writing (and maintaining) custom loop logic every time the sort rules change. There's a simpler way: let Zoho's native column sort do the work.

Deluge — Computed sort field
// Group order = 1, item code = "ABC.01" // Extract last 2 digits of the code: "01" // Concatenate: 1 + 01 = "101" code_suffix = right(input.Item_Code, 2); sort_val = input.Group_Order.toString() + code_suffix; input.Sort_Field = sort_val.toLong(); // Rows now sort natively as 101, 102, 201, 202... // Groups stay together, items ordered within each group

Build one numeric field that already encodes your sort priority, then sort the subform column on that field — zero runtime script cost, and it scales to any number of rows.

4

Use a running balance field to make invalid states impossible

Rather than validating "does the sum of all invoices exceed the quotation?" on every save by re-summing every related record, maintain a running balance field on the parent record. Every time a child record is created, it both reads the current balance and writes the new one.

This turns an expensive cross-record validation into a cheap single-field check, and it means the invalid state — over-invoicing, in this case — simply can't be reached, rather than being caught after the fact.
5

Bound unlimited history with modulo-based slot overwriting

Sometimes you want to keep "the last N values" without letting a related record grow forever — vendor rate revisions, for example. Track an edit counter, then use modulo arithmetic to decide which of N fixed slots gets overwritten next.

Deluge — Circular slot overwrite
edit_count = input.Edit_Count; slot = edit_count % 5; if(slot == 0) { slot = 5; } // mod gives 0 on the 5th, 10th... edit if(slot == 1) { input.Value_1 = new_value; } else if(slot == 2) { input.Value_2 = new_value; } // ...and so on through slot 5 input.Edit_Count = edit_count + 1;

No new records are created, storage stays flat, and you get a bounded, predictable history without a cleanup job.

6

Isolate copies with dual foreign keys instead of duplicating rows

When the same underlying data needs to appear in two different stages of a process (pre-sale and post-sale, draft and final, and so on) — but edits in one stage must never affect the other — the instinct is to copy every row. For subforms with hundreds of rows, that's wasteful and hard to keep in sync.

Instead, give the shared subform two parent-lookup fields, one for each stage. The same physical row displays in both views by default. Only when a row is actually edited in the later stage does the script create a new row tagged to that stage — untouched rows stay shared, edited rows fork automatically.

7

Fail loud in custom functions, not silent

Deluge custom functions called via REST or from another module can fail in ways that are easy to miss — a missing field, an unexpected null, a permission error. Wrapping the core logic in a try/catch and writing a structured error record (function name, input snapshot, timestamp, error message) turns a silent failure into something you can actually debug later, instead of a support ticket with no trail behind it.

Summary

The common thread

Every pattern above does the same basic thing: move a problem from "check for it every time" to "make it structurally impossible." That's true of the loop-prevention flag, the running balance field, the computed sort field, and the dual foreign keys — each replaces repeated runtime logic with a small piece of state that does the same job for free.

01
Flags over re-checks
A single boolean field can replace a whole class of "is this really a user action?" logic.
02
Log as you go
Recording changes at the moment they happen beats reconstructing them later by comparison.
03
Push logic into fields
Computed fields let Zoho's native sort/filter do work you'd otherwise script by hand.
04
Design for scale early
Patterns that don't matter at 20 rows become mandatory at 500 — plan for the larger number.

Working on a Zoho Creator build that's outgrown simple forms?

I build custom Zoho Creator applications end-to-end — from BOM-heavy modules to multi-team approval chains. Happy to talk through what you're running into.

Let's Talk ↗
Written By
Kunwar Pal
Zoho Developer & CRM Solutions Specialist, Delhi, India
Tagged
Deluge Zoho Creator Performance Best Practices
Read the full case study
Solar EPC System →